package com.ztesoft.zsmart.zcm.dialing.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.SSLContext;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Strings;
import com.ztesoft.zsmart.zcm.dialing.service.probe.http.HttpAuthType;
import com.ztesoft.zsmart.zcm.dialing.service.probe.http.HttpClientConfig;

public class HttpClientUtils {

    private static Logger log = LoggerFactory.getLogger(HttpClientUtils.class);

    private static final int DEFAULT_SOCKET_TIMEOUT = 3000;

    private static final int DEFAULT_CONNECT_TIMEOUT = 5000;

    private static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = 3000;

    public static String get(String url) {
        HttpClientConfig clientConfig = new HttpClientConfig("http", HttpAuthType.NO_AUTH, null, null, null, DEFAULT_SOCKET_TIMEOUT);
        CloseableHttpClient httpClient = getHttpClient(clientConfig);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(clientConfig.getTimeout()).setConnectTimeout(HttpClientUtils.DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build();
        return get(url, requestConfig, clientConfig, httpClient);
    }

    public static String get(String url, HttpClientConfig clientConfig) {
        if (null == clientConfig) {
            clientConfig = new HttpClientConfig("http", HttpAuthType.NO_AUTH, null, null, null, DEFAULT_SOCKET_TIMEOUT);
        }
        CloseableHttpClient httpClient = getHttpClient(clientConfig);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(clientConfig.getTimeout()).setConnectTimeout(HttpClientUtils.DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build();
        return get(url, requestConfig, clientConfig, httpClient);
    }

    public static String post(String url, Map<String, String> param) {
        HttpClientConfig clientConfig = new HttpClientConfig("http", HttpAuthType.NO_AUTH, null, null, null, DEFAULT_SOCKET_TIMEOUT);
        CloseableHttpClient httpClient = getHttpClient(clientConfig);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(clientConfig.getTimeout()).setConnectTimeout(HttpClientUtils.DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build();
        return post(url, param, requestConfig, clientConfig, httpClient);
    }

    public static String post(String url, Map<String, String> param, HttpClientConfig clientConfig) {
        if (null == clientConfig) {
            clientConfig = new HttpClientConfig("http", HttpAuthType.NO_AUTH, null, null, null, DEFAULT_SOCKET_TIMEOUT);
        }
        CloseableHttpClient httpClient = getHttpClient(clientConfig);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(clientConfig.getTimeout()).setConnectTimeout(HttpClientUtils.DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build();
        return post(url, param, requestConfig, clientConfig, httpClient);
    }

    public static String postJson(String url, String jsonEntity) {
        HttpClientConfig clientConfig = new HttpClientConfig("http", HttpAuthType.NO_AUTH, null, null, null, DEFAULT_SOCKET_TIMEOUT);
        CloseableHttpClient httpClient = getHttpClient(clientConfig);
        RequestConfig config = RequestConfig.custom().setSocketTimeout(clientConfig.getTimeout()) .setConnectTimeout(HttpClientUtils.DEFAULT_CONNECT_TIMEOUT) .setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build();
        return postJson(url, jsonEntity, config, clientConfig, httpClient);
    }

    public static String postJson(String url, String jsonEntity, HttpClientConfig clientConfig) {
        if (null == clientConfig) {
            clientConfig = new HttpClientConfig("http", HttpAuthType.NO_AUTH, null, null, null, DEFAULT_SOCKET_TIMEOUT);
        }
        CloseableHttpClient httpClient = getHttpClient(clientConfig);
        RequestConfig config = RequestConfig.custom().setSocketTimeout(clientConfig.getTimeout()) .setConnectTimeout(HttpClientUtils.DEFAULT_CONNECT_TIMEOUT) .setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build();
        return postJson(url, jsonEntity, config, clientConfig, httpClient);
    }

    private static String post(String url, Map<String, String> param, RequestConfig requestConfig, HttpClientConfig clientConfig, CloseableHttpClient httpClient) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        CloseableHttpResponse response = null;
        if (HttpAuthType.JWT.equals(clientConfig.getAuthType())) {
            httpPost.addHeader("Authorization", "Bearer " + clientConfig.getToken());
        }
        if (null != param && param.size() > 0) {
            Set<String> keySet = param.keySet();
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            for (String key : keySet) {
                nvps.add(new BasicNameValuePair(key, param.get(key)));
            }
            try {
                httpPost.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8.name()));
            }
            catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        try {
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity);
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        finally {
            try {
                if (response != null) {
                    response.close();
                }
            }
            catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return null;
    }

    private static String postJson(String url, String jsonEntity, RequestConfig requestConfig, HttpClientConfig clientConfig, CloseableHttpClient httpClient) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        if (HttpAuthType.JWT.equals(clientConfig.getAuthType())) {
            httpPost.addHeader("Authorization", "Bearer " + clientConfig.getToken());
        }
        httpPost.addHeader("Content-Type", "application/json;charset=utf-8");
        httpPost.addHeader("Connection", "Keep-Alive");
        CloseableHttpResponse response = null;
        if (jsonEntity != null) {
            httpPost.setEntity(new StringEntity(jsonEntity, Consts.UTF_8.name()));
        }
        try {
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity);
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        finally {
            try {
                if (response != null) {
                    response.close();
                }
            }
            catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    private static String get(String url, RequestConfig requestConfig, HttpClientConfig clientConfig, CloseableHttpClient httpClient) {
        String result = "";
        try {
            if (!url.contains("http://")) {
                url = "http://" + url;
            }
            HttpGet get = new HttpGet(url);
            get.setConfig(requestConfig);
            if (HttpAuthType.JWT.equals(clientConfig.getAuthType())) {
                get.addHeader("Authorization", "Bearer " + clientConfig.getToken());
            }
            CloseableHttpResponse httpResponse = null;
            httpResponse = httpClient.execute(get);
            try {
                HttpEntity entity = httpResponse.getEntity();
                if (null != entity) {
                    result = EntityUtils.toString(entity);
                }
            }
            finally {
                httpResponse.close();
            }
        }
        catch (Exception e) {
            log.error(e.getMessage());
        }
        return result;
    }

    private static CloseableHttpClient getHttpClient(HttpClientConfig clientConfig) {
        CloseableHttpClient client = null;
        HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        try {
            //判断是否是HTTPS请求，如果是，则默认信任所有，免除证书验证
            if (!Strings.isNullOrEmpty(clientConfig.getProtocol()) && clientConfig.enableSsl()) {
                SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                    // 信任所有
                    public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        return true;
                    }
                }).build();
                SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
                clientBuilder.setSSLSocketFactory(csf);
            }
            //HTTP请求身份验证，有两种验证方式，默认不验证，JWT直接放入请求头
            if (HttpAuthType.BASIC_AUTH.equals(clientConfig.getAuthType())) {
                CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(clientConfig.getUser(), clientConfig.getPassword()));
                clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
            client = clientBuilder.build();
        }
        catch (Exception e) {
            log.error("create client error: ", e);
        }
        return client;
    }

}
